<?php


#Here's a few useful tips when working with custom session handlers (and sessions in general - with PHP 5.2.x)...
#
#1) Setting session_set_save_handler() automatically sets the ini setting session.save_handler to 'user' for you - no need to do it yourself.
#
#2) You may in fact not need to set up a __destruct() or use register_shutdown_function() to write your session data if using classes/objects.  My testing with 5.2.x shows that my session data is safely saved without the need of either.  I am unsure if PHP internals have changed to "fix" this and documents just aren't updated with that info, or if perhaps I haven't yet hit a scenario that would trigger this problem.
#
#3) It's stated a few places - but just to re-iterate: you cannot serialize internal PHP objects.  This means if you have a class that extends an internal class (for example, a class that extends PDO for db functions), you will not be able to save an instance of your class in a session variable.  Worst off, you will get a very vague error message about it, saying an unknown exception was thrown and it won't know from where.  The solution is for any class that has to be serialized, don't extend internal classes, instead have them set up an instance of that object internally via __construct() or something, and then use __sleep() and __wakeup() hooks to export/reconfigure your class variables/resources.
#
#4) It appears that since PHP 4.x or somewhere, the seralization of data that takes place for sessions changed.  It used to export human readable text much like serialize() does, however it appears that now the session data is further encoded - quite possibly to hinder session data tampering on the local filesystem.  Just mentioning this if you used to view session data to see what was being saved.


# Note forom user 2
#After about 8 hours of banging my skull on abstract art, it turns out that custom Write function use a blackbox encoding process that isn't represented here!  (Perhaps in session_encode it is?)
#
#When a custom write() method gets the $session_hash and the $data of the session, the $data has binary data in it!  Therefore, stripslashes() or mysql_real_escape_string() will NOT properly prepare a collapsed $_SESSION string for database storage!
#
#Instead, when committing write() $data to a database, it turns out that base64_encode() *WILL* catch all non-character information in the collapsed $_SESSION string, making it's result database-friendly!  (And more filling!)
#
#Don't forget to add base64_decode() during the custom read() method!
#
#debian 5 installs by default the php-suhosin module, which changes the behavior of session_set_save_handler read/write function.



#A bit more information on what patrick at eternalspace dot com stumbled upon (and I have just now as well)...
#
#The "binary" data that is in the session data appears to surround class/object names, and if you pass your session data through a function to sanitize it for SQL injection, you may indeed run in to problems.
#
#For example, using the PDO::quote() function to prepare the data for injection (in my case for SQLite3), it was stopping short as soon as it encountered the first bit of binary data, causing my session information to be corrupted.
#
#This change *must* have happened somewhere in the 5.2 series, because I just started encountering this problem recently on a code base that had been tested & working on earlier versions of PHP 5.2.#
#
#This may in fact be a bug - I have not yet checked... but beware, and perhaps using base64 to encode/decode your session data is a good thing to do just to be sure (though you are now left unable to visually inspect serialized session information at the storage level which is a rather big problem for on-the-fly debugging of sessions).


#I pulled a really stupid move. If you are trying to debug your garbage collector, make sure you call the following >>> BEFORE <<< "session_start":
#ini_set('session.gc_probability', 100);
#ini_set('session.gc_divisor', 100);
#

#When storing sessions in a DB, it's usually beneficial to use an existing custom DB object, but this creates problems with the latest version of PHP 5.3.1. This used to work fine on PHP 5.2.x (Linux and Windows).
#
#The problem now is that session_write_close() is not automatically called when execution ends, but rather after all the objects have been destructed, including the DB object!
#
#There are two ways around this, either manually calling session_write_close() at the end of your script(s), or not using the DB object.
#
#I'm sure this is the intended behavior from the beginning.

#You can call the session_write in db object destructor to be shore that you still have a connection to mysql and the session is write.
#



#CREATE TABLE `sessions` (
#   `id` VARCHAR(32) primary key NOT NULL,
#    `data` TEXT NOT NULL,
#    `timestamp` INT(10) UNSIGNED NOT NULL,
#    KEY `timestamp` (`timestamp`)
#) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


#require_once('site_config.php');
#require_once('commonUtil.php');
#require_once('dbUtil.php');

class SessionHandler_PDOMysql
{
	 private $debug=1;
	 private $log_name = 'SessionHandler_PDOMysql';
	 #blow example and ta summary of these comments.
   #and using the simple native functions of mysql.


    /**
     * a database connection resource
     * @var resource
     */
    private $dbh ;
    private $table = 'sessions';

    /**
     * Open the session
     * @return bool
     */
    public function open() {
      global $site_global_config;
   	 
   	  if ($this->debug)  $this->log_error("open");
      
      list ($db_host, $dbname, $dbuser, $dbpasswd, $persistent) = $site_global_config['SESSION_MYSQL'];
      
      $dsn  = "mysql:host=$db_host;dbname=$dbname;";
	    try {
	    	$h_param = array(
	    	  PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION 
	    	);
	    	if ($persistent){
	    		$h_param[PDO::ATTR_PERSISTENT] = true;
	    	}

	      $this->dbh = new PDO($dsn, $dbuser, $dbpasswd, $h_param);
	       # $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	      return $this->dbh;
	    } catch (PDOException $e) {
	    	echo "DB Connect error";
	      //$this->db_connection_count--; // not probably connected, don not count down...
	      throw new Exception('Unable to connect to db server. Error:'.$e->getMessage(), 1130);
	    }
	    
      return false;
    }

    /**
     * Close the session
     * @return bool
     */
    public function close() {
      if ($this->debug)  $this->log_error("close");
      $this->dbh = null;
      return true;
    }


    /**
     * Read the session
     * @param int session id
     * @return string string of the sessoin
     */
    public function read($id) {
      $result = db_select_one($this->dbh, "select data from ".$this->table." WHERE id=?", $id);
      if ($this->debug)  $this->log_error("read $id $result");
      return $result;
    }
    
    
    

    /**
     * Write the session
     * @param int session id
     * @param string data of the session
     */
    public function write($id, $data) {
      if ($this->debug)  $this->log_error("write $id $data");
      return db_do($this->dbh, "REPLACE INTO ".$this->table." VALUES(?,?,?)", array($id, $data, time()));
    }

    /**
     * Destoroy the session
     * @param int session id
     * @return bool
     */
    public function destroy($id) {
    	if ($this->debug)  $this->log_error("destroy");
      return db_do($this->dbh, "DELETE FROM ".$this->table." WHERE id=?", $id);
    }

    /**
     * Garbage Collector
     * @param int life time (sec.)
     * @return bool
     * @see session.gc_divisor      100
     * @see session.gc_maxlifetime 1440
     * @see session.gc_probability    1
     * @usage execution rate 1/100
     *        (session.gc_probability/session.gc_divisor)
     */
    public function gc($max) {
    	if ($this->debug)  $this->log_error("gc");
      return db_do($this->dbh, "DELETE FROM ".$this->table." WHERE timestamp<?", time() - $max);
    }


   private function log_error($msg){
   	 global $site_global_config;
   	 $now = date("m-d-y H:i:s");
     $path = $site_global_config['LOG'].$this->log_name.'.log';
     AppendFile($path, "$now\t$msg\n");
  }
}
//
////ini_set('session.gc_probability', 50);
//ini_set('session.save_handler', 'user');
//
//$session = new Session();
//session_set_save_handler(array($session, 'open'),
//                         array($session, 'close'),
//                         array($session, 'read'),
//                         array($session, 'write'),
//                         array($session, 'destroy'),
//                         array($session, 'gc'));
//
//// below sample main
//
//session_start();
//session_regenerate_id(true);
//
//if (isset($_SESSION['counter'])) {
//    $_SESSION['counter']++;
//} else {
//    $_SESSION['counter'] = 1;
//}

?>